#include <errno.h>
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/eventfd.h>


#include "EventLoop.h"
#include "Acceptor.h"
#include "TcpConnection.h"

EventLoop::EventLoop(Acceptor& acceptor)
:_epfd(createEpollFd()),
_evtList(1024),
_isLooping(false),
_acceptor(acceptor),
_evtfd(createEventFd())
{
    //将监听的文件描述符加入到epoll红黑树中监听
    addEpollReadFd(_acceptor.fd());
    //将用于唤醒epoll_wait的文件描述符加入到epoll红黑树中监听
    addEpollReadFd(_evtfd);


}
EventLoop::~EventLoop()
{
    close(_epfd);
    close(_evtfd);
}
void EventLoop::loop()
{
    _isLooping=true;

    while (_isLooping)
    {
        waitEpollFd();
    }

}
void EventLoop::unLoop()
{
    _isLooping=false;
}
void EventLoop::setNewConnectionCallback(TcpConnectionCallback &&cb)
{
    _onNewConnectionCallback=std::move(cb);
}
void EventLoop::setMessageCallback(TcpConnectionCallback &&cb)
{
    _onMessageCallback=std::move(cb);
}
void EventLoop::setCloseCallback(TcpConnectionCallback &&cb)
{
    _onCloseCallback=std::move(cb);
}


    //创建epoll文件描述符
int EventLoop::createEpollFd()
{
    int epfd = epoll_create1(0);
    
    if (-1== epfd){
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }
    return epfd;
}
//等待epoll事件的触发
void EventLoop::waitEpollFd()
{
    /**
     * TOASK 这里的epoll_wait第二个地址怎么写呢
     * answer 已解决&(*_evtList.begin())
     * */
    int nready;
    do
    {
        nready = epoll_wait(_epfd,&(*_evtList.begin()), _evtList.size(),3000);
        
    } while (-1==nready&&errno==EINTR);//在超时或者被信号中断的情况下，继续等待

    //只要跳出来还是nready==-1那表明就是有错误
    if(-1==nready)
    {
        perror("epoll_wait error");
        exit(EXIT_FAILURE);
    }
    else if(0==nready){
        //超时
        std::cout<<">> epoll_wait timeout"<<std::endl;
    }
    else
    {
        /*此处需要检查触发的文件描述符的数量是否达到1024，如果达到了，那么就需要扩容*/
        if (nready==(int)_evtList.size())
        {
            _evtList.reserve(2*nready);
        }
        
        //遍历每个epoll_event
        for (int i = 0; i < nready; i++)
        {
            int fd = _evtList[i].data.fd;
            //fd为listenfd，表示有新的连接
            if (fd==_acceptor.fd())
            {
                /*判断触发的事件是否是读事件*/
                if (_evtList[i].events&EPOLLIN)
                {
                    handleNewConnection();
                }
                
            }
            else if (fd==_evtfd){//fd是_evtfd，表示有新的通知
                //TOASK 如果fd是_evtfd该怎么处理呢
                //answer 如果fd是_evtfd，那么就调用handleRead()函数
                if (_evtList[i].events&EPOLLIN)
                {
                    handleRead();
                    //将数据发送出去
                    doPendingFunctors();
                }
                
            }
            else
            {
                //fd是普通的文件描述符，表示有数据可读
                /*判断触发的事件是否是读事件*/
                if (_evtList[i].events&EPOLLIN)
                {
                    handleMessage(fd);
                }
            }
            
        }
    }

    
    

}
//等待epoll事件的触发
void EventLoop::addEpollReadFd(int fd)
{

    struct epoll_event evt;
    evt.events=EPOLLIN;
    evt.data.fd=fd;
    int ret = epoll_ctl(_epfd,EPOLL_CTL_ADD,fd,&evt);
    if (-1==ret){
        perror("epoll_ctl add");
        exit(EXIT_FAILURE);
    }
}
//等待epoll事件的触发
void EventLoop::removeEpollReadFd(int fd)
{
    int ret = epoll_ctl(_epfd,EPOLL_CTL_DEL,fd,nullptr);
    if (-1==ret){
        perror("epoll_ctl delete");
        exit(EXIT_FAILURE);
    }
}

void EventLoop::handleNewConnection()
{
    int fd = _acceptor.accept();
    if (-1==fd){
        perror("accept");
        exit(EXIT_FAILURE);
    }
    TcpConnectionPtr conn = std::make_shared<TcpConnection>(fd,this);
    _conns[fd]=conn;
    //将新建立的连接添加至epoll红黑树监听
    addEpollReadFd(fd);
    conn->setOnNewConnection(_onNewConnectionCallback);
    conn->setMessageCallback(_onMessageCallback);
    conn->setCloseCallback(_onCloseCallback);

    //连接建立成功，调用连接建立回调函数
    conn->handleNewConnectionCallback();

}
void EventLoop::handleMessage(int fd)
{

    auto it = _conns.find(fd);
    if (it!=_conns.end()){
        //连接存在
        bool isClose = it->second->isClosed();//先判断连接是否已经关闭

        if (isClose){
            //连接已断开
            it->second->handleCloseCallback();
            //将fd从红黑树上摘下
            removeEpollReadFd(fd);
            //将fd从map中删除
            _conns.erase(it);
        }
        else
        {
            //连接未断开，调用TcpConnection的处理消息回调函数
            
            /**
             * TOASK 这里读取失败了怎么判定呢
             * answer 上面的isClosed()已经判断了连接是否关闭了，
             *         如果连接关闭了，那么就不需要再调用handleMessageCallback()了
             * */
            it->second->handleMessageCallback();
        }
    }else{
        //连接不存在
        std::cout<<">>fd="<<fd<<" not found in connections"<<std::endl;
        return;

    }
}



void EventLoop::handleRead()
{
    uint64_t counter;
    int ret = ::read(_evtfd,&counter,sizeof(uint64_t));
    if (sizeof(uint64_t)!=ret)
    {
        perror("handleRead errror");
        exit(EXIT_FAILURE);
    }
    
}


void EventLoop::doPendingFunctors()
{
    std::vector<Functor> tasks;
    {
        std::lock_guard<std::mutex> lock(_pendingTasksMutex);
        tasks.swap(_pendingTasks);
    }
    //调用for循环将任务执行
    for(auto &task:tasks){
        task();
    }

}
void EventLoop::wakeup()
{
    uint64_t _one = 1;
    int ret = ::write(_evtfd,&_one,sizeof(uint64_t));
    if (sizeof(uint64_t)!=ret)
    {
        perror("wakeup error");
        exit(EXIT_FAILURE);
    }
    
}
void EventLoop::runInLoop(Functor&& cb)
{

    {
        std::lock_guard<std::mutex> lock(_pendingTasksMutex);
        _pendingTasks.push_back(std::move(cb));
    }


    wakeup();
}

int EventLoop::createEventFd()
{
    int evtfd = ::eventfd(0,0);
    if (-1==evtfd){
        perror("eventfd");
        exit(EXIT_FAILURE);
    }
    return evtfd;
}